
项目背景
nodejs项目，webpack打包，用axios请求，Promise封装，nunjucks模板引擎；
之前已将nunjucks模板通过webpack打包策略，做成前后端共用；
目前需要将网络请求以及数据处理封装成service模块；
目录划分：

如上图所示：
将公共代码放到service中，整合两端共同的一些网络请求以及数据处理（node首屏，客户端再次请求数据更新等操作）
&nbsp;
这里碰到的两个问题：
1. node模块使用module.exports，而webpack我们使用的是import/export，两者共用会报错；
2.我们使用了Promise做了两层封装（service封装、service中的fetch封装：抹平node和客户端的环境差异）
&nbsp;
第一个问题，其实webpack也提供了module.exports的方法，所以两端的模块是可以共用的。
而第二个问题，我们使用了Promise，也在webpack全局引入了babel-polyfill，但是会报错：

Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

导致前面的排查思路一直以为是module.exports出了问题；
&nbsp;
解决方法
我们刚开始是通过引入es6-promise来解决的：
service/fetch.js:

var axios = require('axios');
var Promise = require('es6-promise').Promise;

module.exports = function(opts, request) {
  return new Promise(function(resolve, reject) {
    axios(opts).then(function(res) {
      res = res.data
      if (res.success) {
        resolve(res)
      } else {
        reject({ ___req: opts, ___res: res })
      }
    }).catch(function(err) {
      reject({ ___req: opts, ___res: err.data || err.stack || err })
    })
  })
}

&nbsp;
service/wawa.js

var fetch = require('./fetch');
var Promise = require('es6-promise').Promise;

var getGamelist = function(params, req) {
  return new Promise(function(resolve, reject) {
    fetch({
      url: '/api/appeal/appealJoinOrderPage',
      type: 'get',
      params: params
    }).then(function(res) {
      resolve(res.data)
    }).catch(function(err) {
      reject(err)
    })
  })
}

module.exports = {
  getGamelist
}

&nbsp;
并且我们也尝试在全局引入es6-promise，仍然报错；
这样我们暂时得出结论，是原生Promise语法，直接与module.exports冲突报错。目前只能通过在当前js中引入es6-promise来规避。
所幸的是，每个js中重复引入的es6-promise，在最终webpack打包的时候会去重，也避免了打包体积变大的问题。
至此，node前后端代码共用的方案暂时通过。并且后面还可以写除了service以外的共用代码，提升了复用性和可维护性。
&nbsp;
再次踩到坑
我们在迁移另一个公用service的时候，又碰到原来的问题，精简后的代码如下：

/**
 * 获取红包列表
 */
var getRedList = function(params, req) {
  return JSON.stringify({a:1})
}

module.exports = {
  getRedList
}

纳尼？又报错

Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'

经过排查定位，发现是JSON.stringify不支持。。。但平时我们正常使用export/import从未碰到此问题。
所以猜测是module.exports出去的模块，在webpack中默认是不会给全局方法加上window的。
&nbsp;
最终解决：兼容global和window（其实还不是最终）
那解决方法就容易了，给全局方法手动加上全局对象，兼容处理global和window就可以了：

(function(global) {
  let isBrowser = global.toString() === '[object Window]';
  /**
   * 获取红包列表
   */
  var getRedList = function(params, req) {
    return JSON.stringify({a:1})
  }

  module.exports = {
    getRedList
  }
})(typeof exports === 'undefined' ? global = window : global);

&nbsp;
以上，得出在node和浏览器webpack共用模块化代码的解决方案：
1. 使用 module.exports / require 做模块化
2. 兼容处理global和window
&nbsp;
打死不改终极奥义最终版
上周末线上突然报错飙升，发现集中在安卓4.3以下，报错：Promise is not defined
经过不断试错排查，发现在module.exports出去后，里边的 'es6-promise'兼容包，在外面是不生效的。
最后决定在外边定义一个全局的Promise:
&nbsp;

window.Promise = require('es6-promise').Promise;

&nbsp;
此外，在低版本安卓中，判断window环境还有一个坑：
需要这么写

(function(global) {
  module.exports = function(opts, request) {
    var isBrowser = global.toString() === '[object Window]' || global.toString() === '[object DOMWindow]';

    return new global.Promise(function(resolve, reject) {
      if (isBrowser) {
        axios(opts).then(function(res) {

        }).catch(function(err) {
        })
      } else {
        axios(opts).then(function(res) {
          
        }).catch(function(err) {
        })
      }
    })
  }
})(typeof exports === 'undefined' ? global = window : global);

标红的那段是重点，划下来！！！
End
写到这里，已经做好了node项目代码复用的基础，那么整个接口的数据流程是怎么样的呢，又会碰到什么样的问题？
比如我们需要在两端调用service的时候必须获得同样的数据格式，而浏览器的请求实际是经过一次node接口转发，总共两次fetch流程产生的。
而且fetch模块，又需要支持浏览器和node的直接调用。所以我们整理出下面的接口请求流程图：

&nbsp;
具体项目架构会在下一期文章给出。
